using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using System.CodeDom.Compiler;

using Lds.Ward.PhotoDirectory.Logic;

namespace Lds.Ward.PhotoDirectory.App
{
	public partial class Wizard : Form
	{
		public Wizard()
		{
			InitializeComponent();
			nextClipboardViewer = WinAPI.SetClipboardViewer(Handle);
			Disposed += new EventHandler(Wizard_Disposed);
		}

		void Wizard_Disposed(object sender, EventArgs e)
		{
			WinAPI.ChangeClipboardChain(this.Handle, nextClipboardViewer);
		}

		private IntPtr nextClipboardViewer = IntPtr.Zero;

		protected override void WndProc(ref Message m)
		{
			switch ((WinAPI.WindowsMessages)m.Msg)
			{
				case WinAPI.WindowsMessages.WM_DRAWCLIPBOARD:
					ClipboardChanged();
					// Forward the message down the chain
					WinAPI.SendMessage(nextClipboardViewer, (WinAPI.WindowsMessages)m.Msg, m.WParam, m.LParam);
					break;
				case WinAPI.WindowsMessages.WM_CHANGECBCHAIN:
					if (m.WParam == nextClipboardViewer)
						nextClipboardViewer = m.LParam;
					else
						WinAPI.SendMessage(nextClipboardViewer, (WinAPI.WindowsMessages)m.Msg, m.WParam, m.LParam);
					break;
				default:
					base.WndProc(ref m);
					break;
			}
		}

		private void ClipboardChanged()
		{
			if (!watchingClipboard) return;
			if (IsValidImportData(Clipboard.GetText()))
			{
				Process p = FindMlsProcess();
				if (p != null)
					WinAPI.SendMessage(p.MainWindowHandle, WinAPI.WindowsMessages.WM_SYSCOMMAND,
						(IntPtr)WinAPI.WM_SYSCOMMAND_WPARAM.SC_MINIMIZE, IntPtr.Zero);
				WindowState = FormWindowState.Normal;
				ImportFromString(Clipboard.GetText());
			}
		}

		private bool IsValidImportData(string familiesText)
		{
			try
			{
				IList<Family> families = FlatFile.ToFamilies(new StringReader(familiesText));
				return families.Count > 0;
			}
			catch
			{
				return false;
			}
		}

		IList<Family> Families;
		IList<Family> CheckedFamilies
		{
			get
			{
				List<Family> checkedFamilies = new List<Family>(filterListView.CheckedItems.Count);
				foreach (int idx in filterListView.CheckedIndices)
					checkedFamilies.Add(Families[idx]);
				return checkedFamilies.AsReadOnly();
			}
		}
		IList<Family> CheckedFamiliesWithPhotos
		{
			get
			{
				List<Family> checkedFamilies = new List<Family>(filterListView.CheckedItems.Count);
				foreach (int idx in filterListView.CheckedIndices)
					if (Families[idx].HasPhoto(PhotosDirectory))
						checkedFamilies.Add(Families[idx]);
				return checkedFamilies.AsReadOnly();
			}
		}

		private void Tabs_SelectedIndexChanged(object sender, EventArgs e)
		{
			btnNext.Enabled = Tabs.SelectedIndex < Tabs.TabPages.Count - 1;
			btnBack.Enabled = Tabs.SelectedIndex > 0;
		}

		private void btnNext_Click(object sender, EventArgs e)
		{
			Tabs.SelectedIndex++;
		}

		private void btnPrevious_Click(object sender, EventArgs e)
		{
			Tabs.SelectedIndex--;
		}

		private void btnImportFromFile_Click(object sender, EventArgs e)
		{
			if (ImportMlsFileDialog.ShowDialog() != DialogResult.OK) return;
			lblImportFilename.Text = AbbreviatePath(ImportMlsFileDialog.FileName, 40);

			using (StreamReader sr = new StreamReader(ImportMlsFileDialog.OpenFile()))
			{
				ImportFromString(sr.ReadToEnd());
			}
		}

		private void btnImportFromClipboard_Click(object sender, EventArgs e)
		{
			// See if clipboard is already filled with appropriate MLS data.
			bool clipboardDataPresent = Clipboard.ContainsText(TextDataFormat.Text);
			// See if data is in expected format.
			bool clipboardDataValid = IsValidImportData(Clipboard.GetText());

			if (clipboardDataPresent && clipboardDataValid)
				ImportFromString(Clipboard.GetText());
			else
				GetDataFromMls();
		}

		bool watchingClipboard = false;

		private void GetDataFromMls()
		{
			// Hook to a clipboard event so we know when copy to clipboard has been done.
			watchingClipboard = true;

			// Get out of the way so the user can switch to MLS.
			WindowState = FormWindowState.Minimized;
			MlsCopyClipboardNotifyIcon.Visible = true;
			MlsCopyClipboardNotifyIcon.ShowBalloonTip(10 * 1000);

			// Activate MLS app if it is already running.
			Process mls = FindMlsProcess();
			if (mls != null)
				WinAPI.SendMessage(mls.MainWindowHandle, WinAPI.WindowsMessages.WM_SYSCOMMAND,
					(IntPtr)WinAPI.WM_SYSCOMMAND_WPARAM.SC_MAXIMIZE, IntPtr.Zero);
		}

		private Process FindMlsProcess()
		{
			Process[] mls = Process.GetProcessesByName("MLS");
			if (mls.Length == 0) return null;
			return mls[0];
		}

		private void ImportFromString(string familiesText)
		{
			using (BusyDialog busy = new BusyDialog(this, "Importing records and applying photos"))
			{
				Families = FlatFile.ToFamilies(new StringReader(familiesText));

				PhotosPreview.Clear();
				foreach (Family f in Families)
				{
					string imageKey = f.Surname + ", " + f.FamilyMembers[0].GivenNames;
					PhotosPreview.Items.Add(f.Surname, imageKey);
					ListViewItem item = new ListViewItem();
					item.ImageKey = f.Surname;
					item.Text = f.Surname;
					item.SubItems.AddRange(new string[] {
						f.FamilyMembers[0].GivenNames,
						f.MailingAddress.City,
						f.MailingAddress.State,
						f.MailingAddress.PostalCode,
					});
					item.Checked = true;
					filterListView.Items.Add(item);
				}

				lblMembershipRecordsImportedCount.Text = Families.Count.ToString();
				GenerateThumbnailsForPreview(busy);
			}
		}

		private void Wizard_Activated(object sender, EventArgs e)
		{
			MlsCopyClipboardNotifyIcon.Visible = false;
			watchingClipboard = false;
		}

		private void cancelAndReturnToProgramToolStripMenuItem_Click(object sender, EventArgs e)
		{
			WindowState = FormWindowState.Normal;
			Focus();
		}

		private void tellMeAgainToolStripMenuItem_Click(object sender, EventArgs e)
		{
			MlsCopyClipboardNotifyIcon.ShowBalloonTip(10 * 1000);
		}

		private void MlsCopyClipboardNotifyIcon_DoubleClick(object sender, EventArgs e)
		{
			WindowState = FormWindowState.Normal;
			Focus();
		}

		private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
		{

		}

		private void btnPickPhotosDirectory_Click(object sender, EventArgs e)
		{
			if (PhotosDirectory != null)
				PhotoDirectoryPicker.SelectedPath = PhotosDirectory.LocalPath;
			if (PhotoDirectoryPicker.ShowDialog() != DialogResult.OK) return;
			PhotosDirectory = new Uri(PhotoDirectoryPicker.SelectedPath);
			using (BusyDialog busy = new BusyDialog(this, "Applying photos and generating thumbnails"))
				GenerateThumbnailsForPreview(busy);
		}

		private Uri photosDirectory;
		private Uri PhotosDirectory
		{
			get
			{
				return photosDirectory;
			}
			set
			{
				if (value != null)
				{
					if (!Directory.Exists(value.LocalPath))
						throw new DirectoryNotFoundException("The directory \"" + value.LocalPath + "\" could not be found.");
					if (!value.LocalPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
						value = new Uri(value.LocalPath + Path.DirectorySeparatorChar);
				}
				photosDirectory = value;
				lblPhotosDirectory.Text = value != null ? AbbreviatePath(value.LocalPath, 40) : string.Empty;
			}
		}

		private void GenerateThumbnailsForPreview(BusyDialog busy)
		{
			PhotosPreviewImageList.Images.Clear();
			if (Families == null || PhotosDirectory == null) return;
			if (busy != null) busy.WorkSize = Families.Count;
			// generate thumbnails
			foreach (Family f in Families)
			{
				busy.Progress++;
				string imagePath = f.GetPhotoPath(PhotosDirectory);
				if (imagePath == null) continue;
				using (Image original = new Bitmap(imagePath))
				{
					Image thumbnail = new Bitmap(original, PhotosPreviewImageList.ImageSize);
					Graphics g = Graphics.FromImage(thumbnail);
					g.CompositingQuality = CompositingQuality.HighQuality;
					g.SmoothingMode = SmoothingMode.HighQuality;
					g.DrawImage(original, new Rectangle(0, 0, thumbnail.Size.Width, thumbnail.Size.Height));
					PhotosPreviewImageList.Images.Add(f.Surname + ", " + f.FamilyMembers[0].GivenNames, thumbnail);
				}
			}
		}

		private void btnSave_Click(object sender, EventArgs e)
		{
			Properties.Settings.Default.PhotosDirectory = PhotosDirectory != null ? PhotosDirectory.LocalPath : null;
			Properties.Settings.Default.Save();
			Close();
		}

		private void btnFinish_Click(object sender, EventArgs e)
		{
			Tabs.SelectedTab = OutputTabPage;
		}

		private void Wizard_Load(object sender, EventArgs e)
		{
			if (!string.IsNullOrEmpty(Properties.Settings.Default.PhotosDirectory))
				PhotosDirectory = new Uri(Properties.Settings.Default.PhotosDirectory);
		}

		private string AbbreviatePath(string path, int maxLength)
		{
			if (path.Length > maxLength)
			{
				int lastSlash = path.LastIndexOf(Path.DirectorySeparatorChar, path.Length - 2);
				string rootPath = Path.GetPathRoot(path);
				if (rootPath.Length == lastSlash)
					return path;
				else
					return Path.GetPathRoot(path) + "..." + path.Substring(lastSlash);
			}
			return path;
		}

		private void btnProduceScreenPdf_Click(object sender, EventArgs e)
		{
			ProducePdf(MediaQuality.Screen);
		}

		private void btnProducePrinterPdf_Click(object sender, EventArgs e)
		{
			ProducePdf(MediaQuality.Print);
		}

		private void btnProduceHQPdf_Click(object sender, EventArgs e)
		{
			ProducePdf(MediaQuality.Maximum);
		}

		private void ProducePdf(MediaQuality quality)
		{
			if (Families == null || Families.Count == 0)
			{
				MessageBox.Show(this, "Import membership records first.", "PDF Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
				Tabs.SelectedTab = ImportTabPage;
				return;
			}
			if (PhotosDirectory == null)
			{
				MessageBox.Show(this, "Choose a directory to find photos first.", "PDF Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
				Tabs.SelectedTab = PhotosTabPage;
				return;
			}
			if (SavePdfDialog.ShowDialog() != DialogResult.OK) return;
			using (Stream s = SavePdfDialog.OpenFile())
			{
			retry:
				try
				{
					using (BusyDialog busy = new BusyDialog(this, "Generating PDF", PdfPagesEstimate))
					{
						PdfProgressUpdater updater = new PdfProgressUpdater(busy);
						FamiliesToDirectoryPdf.Convert(CheckedFamilies, unitNameBox.Text,
							PhotosDirectory, informationHiding1.AreaCode,
							informationHiding1.Address, s, quality, new org.apache.fop.apps.Driver.UpdateStatusCallback(updater, "UpdatePage"));

						Properties.Settings.Default.Save();
					}
				}
				catch (Exception ex)
				{
					if (MessageBox.Show(this, ex.Message, "An error occurred", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Retry)
						goto retry;
				}
			}

			// Open the PDF for immediate viewing.
			Process.Start(SavePdfDialog.FileName);
		}

		class PdfProgressUpdater
		{
			public PdfProgressUpdater(BusyDialog busy)
			{
				this.busy = busy;
			}
			BusyDialog busy;
			public void UpdatePage(int page)
			{
				if (page <= busy.WorkSize)
				{
					busy.Progress = page;
					Application.DoEvents();
					System.Threading.Thread.Sleep(1);
				}
			}
		}

		private int PdfPagesEstimate
		{
			get
			{
				return Families.Count / 9 + (Families.Count % 9 > 0 ? 1 : 0);
			}
		}

		private void btnUploadImages_Click(object sender, EventArgs e)
		{
			if (Families == null || Families.Count == 0)
			{
				MessageBox.Show(this, "Import membership records first.", "PDF Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
				Tabs.SelectedTab = ImportTabPage;
				return;
			}
			if (PhotosDirectory == null)
			{
				MessageBox.Show(this, "Choose a directory to find photos first.", "PDF Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
				Tabs.SelectedTab = PhotosTabPage;
				return;
			}

			AuthenticationDialog login = new AuthenticationDialog();
			if (login.ShowDialog() != DialogResult.OK) return;

			using (BusyDialog busy = new BusyDialog(this, "Uploading images to ward web site", Families.Count))
			{
				PdfProgressUpdater updater = new PdfProgressUpdater(busy);

				FamiliesToWebSite.Convert(Families, PhotosDirectory,
					login.Username, login.Password, new FamiliesToWebSite.UpdateStatusCallback(updater.UpdatePage));
			}
		}

		private void btnBulletinBoardPictures_Click(object sender, EventArgs e)
		{
			if (Families == null || Families.Count == 0)
			{
				MessageBox.Show(this, "Import membership records first.", "PDF Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
				Tabs.SelectedTab = ImportTabPage;
				return;
			}
			if (PhotosDirectory == null)
			{
				MessageBox.Show(this, "Choose a directory to find photos first.", "PDF Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
				Tabs.SelectedTab = PhotosTabPage;
				return;
			}
			if (CheckedFamiliesWithPhotos.Count == 0)
			{
				MessageBox.Show(this, "No selected families have associated photos.");
				Tabs.SelectedTab = PhotosTabPage;
				return;
			}
			SizeF photoSize = new SizeF((float)photoBoardWidth.Value, -1);

			using (TempFileCollection tfc = new TempFileCollection())
			{
				string pdfFileName = tfc.AddExtension("pdf", true);
				using (Stream s = new FileStream(pdfFileName, FileMode.Create))
				{
				retry:
					try
					{
						using (BusyDialog busy = new BusyDialog(this, "Generating PDF", PdfPagesEstimate))
						{
							PdfProgressUpdater updater = new PdfProgressUpdater(busy);
							FamiliesToPhotoBoardPdf.Convert(CheckedFamiliesWithPhotos,
								unitNameBox.Text, photoSize, PhotosDirectory, s,
								new org.apache.fop.apps.Driver.UpdateStatusCallback(updater, "UpdatePage"));

							Properties.Settings.Default.Save();
						}
					}
					catch (Exception ex)
					{
						if (MessageBox.Show(this, ex.Message, "An error occurred", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Retry)
							goto retry;
					}
				}

				// Open the PDF for immediate viewing.
				Process.Start(pdfFileName);
			}
		}

		private void thumbnailViewButton_Click(object sender, EventArgs e)
		{
			filterListView.View = View.LargeIcon;
			thumbnailViewButton.Checked = true;
			detailsViewButton.Checked = false;
		}

		private void detailsViewButton_Click(object sender, EventArgs e)
		{
			filterListView.View = View.Details;
			thumbnailViewButton.Checked = false;
			detailsViewButton.Checked = true;
		}

		private void selectAllButton_Click(object sender, EventArgs e)
		{
			this.Cursor = Cursors.WaitCursor;
			try
			{
				bool selectAllCheckedState = selectAllButton.Checked;
				foreach (ListViewItem item in filterListView.Items)
					item.Checked = !selectAllCheckedState;
				selectAllButton.Checked = !selectAllCheckedState;
			}
			finally
			{
				this.Cursor = Cursors.Default;
			}
		}

		private void filterListView_ItemChecked(object sender, ItemCheckedEventArgs e)
		{
			bool allChecked = true;
			bool allUnchecked = true;
			foreach (ListViewItem item in filterListView.Items)
			{
				allChecked &= item.Checked;
				allUnchecked &= !item.Checked;
			}
			selectAllButton.Checked = allChecked;
		}
	}
}